/*
    该模块基于开闭原则
    *目的就是建立消息类型与业务回调函数的映射关系
    *如果后期新增功能，不修改以前的代码，只需要新增映射关系就可以
*/

#pragma once

#include <unordered_map>
#include <mutex>
#include "net.hpp"
#include "message.hpp"

namespace JsonMuduoRPC
{
    class BaseCallBack
    {
    public:
        using ptr = std::shared_ptr<BaseCallBack>;
        virtual void onMessage(const BaseConnection::ptr &con, const BaseMessage::ptr msg) = 0;
    };

    template <typename T>
    class CallBackT : public BaseCallBack
    {
    public:
        using ptr = std::shared_ptr<CallBackT<T>>;
        using messageCallBack = std::function<void(const BaseConnection::ptr &, const std::shared_ptr<T> &)>;

        CallBackT(const messageCallBack &handler) : _handler(handler)
        {
        }
        void onMessage(const BaseConnection::ptr &con, const BaseMessage::ptr msg) override
        {
            auto type_msg = std::dynamic_pointer_cast<T>(msg);
            _handler(con, type_msg);
        }

    private:
        messageCallBack _handler;
    };
    class Dispatcher
    {
    public:
        using ptr = std::shared_ptr<Dispatcher>;

        template <typename T>
        void registerHandler(const MType &mType, const typename CallBackT<T>::messageCallBack &handler)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            typename CallBackT<T>::ptr cb = std::make_shared<CallBackT<T>>(handler);
            _handler_map.insert(std::make_pair(mType, cb));
        }

        void onMessage(const BaseConnection::ptr &con, const BaseMessage::ptr msg)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _handler_map.find(msg->getMType());
            if (it != _handler_map.end())
            {
                it->second->onMessage(con, msg);
            }
            else
            {
                LOG(ERROR, "未知类型的消息，连接断开");
                con->shutdown();
            }
        }

    private:
        std::unordered_map<MType, BaseCallBack::ptr> _handler_map;
        std::mutex _mutex;
    };

} // namespace JsonMuduoRPC
